﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;

namespace FBGraph.ClientGenerator.CodeGen
{
    public class CodeGenObjectList<T> : List<T>, ICodeGenObject
    {
        public CodeGenObjectList(params T[] items) : this(null, 0, items) { }

        public CodeGenObjectList(String separator, params T[] items) : this(separator, 0, items) { }

        public CodeGenObjectList(Int32 lineBreaks, params T[] items) : this(null, lineBreaks, items) { }

        private CodeGenObjectList(String separator, Int32 lineBreaks, IEnumerable<T> items)
        {
            this.LineBreaks = lineBreaks;
            this.Separator = separator;
            if (items != null && items.Any()) this.AddRange(items);
        }

        public virtual String Separator { get; set; }
        public virtual Int32 LineBreaks { get; set; }

        private static Boolean? _implementsICodeGenObject;
        private static Boolean ImplementsICodeGenObject
        {
            get
            {
                if (!_implementsICodeGenObject.HasValue)
                {
                    _implementsICodeGenObject = typeof(T).GetInterfaces().Any(i => i == typeof(ICodeGenObject));
                }
                return _implementsICodeGenObject.Value;
            }
        }

        public virtual void Write(CodeGenWriter writer)
        {
            if (this.Any())
            {
                var last = this.Last();
                this.ForEach(obj =>
                {
                    if (ImplementsICodeGenObject) writer.Write((ICodeGenObject)obj);
                    else writer.Write((Object)obj);
                    if (!obj.Equals(last))
                    {
                        if (!String.IsNullOrEmpty(this.Separator)) writer.Write(this.Separator);
                        for (var lb = 1; lb <= this.LineBreaks; lb++) writer.WriteLine();
                    }
                });
            }
        }

        public override Boolean Equals(Object obj)
        {
            var that = obj as CodeGenObjectList<T>;
            if (that == null) return false;
            else return
                this.Separator == that.Separator &&
                this.LineBreaks == that.LineBreaks &&
                this.SequenceEqual(that);
        }

        public override Int32 GetHashCode()
        {
            var hashCode =
                this.LineBreaks.GetHashCode() ^
                this.Separator.GetHashCode();

            foreach (var item in this) hashCode ^= item.GetHashCode();

            return hashCode;
        }

        public override String ToString()
        {
            return CodeGenObject.ToString(this);
        }
    }
}